#include "../include/SqlConnection.h"

SqlConnection::SqlConnection()
{
    mysql = mysql_init(NULL);
    if (mysql == NULL)
    {
        std::cout << "mysql_init error : insufficient memory" << std::endl;
    }
}

SqlConnection::~SqlConnection()
{
    disconnect();
}

bool SqlConnection::is_open()
{
    return mysql_ping(mysql) == 0;
}

void SqlConnection::disconnect()
{
    mysql_close(mysql);
    mysql = mysql_init(NULL);
    result = NULL;
    host.clear();           
    user.clear();           
    passwd.clear();         
    db.clear();             
}

bool SqlConnection::connect()
{
    /* 防止复用已经打开的连接 */
    if (is_open())
    {
        disconnect();
    }

    Json::Reader reader;
    Json::Value root;

    std::fstream in("../setting.json");

    if (!in.is_open()) 
    {
        std::cout << "error to open file" << std::endl;
        return false;
    }

    if (reader.parse(in, root))
    {
        host = root["mysql"]["host"].asString();
        user = root["mysql"]["user"].asString();
        passwd = root["mysql"]["passwd"].asString();
        db = root["mysql"]["db"].asString();
        port = root["mysql"]["port"].asUInt();
    }

    in.close();
    
    if (!mysql_real_connect(mysql, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, NULL, 0))
    {
        std::cout << "fail to connect"  << std::endl;
        return false;
    }
    
    return true;
}

bool SqlConnection::create_table(const char* stmt_str)
{
    if (mysql_query(mysql, stmt_str))
    {
        std::cout << "fail to create table" << std::endl;
        return false;
    }
    return true;
}

bool SqlConnection::create_table(const std::string& stmt_str)
{
    return create_table(stmt_str.c_str());
}

bool SqlConnection::insert(const char* stmt_str)
{
    if (mysql_query(mysql, stmt_str))
    {
        std::cout << "fail to insert" << std::endl;
        return false;
    }
    return true;
}

bool SqlConnection::insert(const std::string& stmt_str)
{
    return insert(stmt_str.c_str());
}

bool SqlConnection::update(const char* stmt_str)
{
    if (mysql_query(mysql, stmt_str))
    {
        std::cout << "fail to update" << std::endl;
        return false;
    }
    return true;
}

bool SqlConnection::update(const std::string& stmt_str)
{
    return update(stmt_str.c_str());
}

bool SqlConnection::remove(const char* stmt_str)
{
    if (mysql_query(mysql, stmt_str))
    {
        std::cout << "fail to remove" << std::endl;
        return false;
    }
    return true;
}

bool SqlConnection::remove(const std::string& stmt_str)
{
    return remove(stmt_str.c_str());
}

std::vector<std::string> SqlConnection::query(const char* stmt_str)
{
    std::vector<std::string> field_names;
    if (mysql_query(mysql, stmt_str))
    {
        std::cout << mysql_error(mysql) << std::endl;
        return field_names;
    }

    result = mysql_use_result(mysql);

    MYSQL_FIELD* field = NULL;
    do {
        field = mysql_fetch_field(result);
        if (field) field_names.emplace_back(field->name);
    } while (field);

    return field_names;
}

std::vector<std::string> SqlConnection::query(const std::string& stmt_str)
{
    return query(stmt_str.c_str());
}

std::vector<std::string> SqlConnection::next()
{
    std::vector<std::string> ret;
    MYSQL_ROW row = mysql_fetch_row(result);
    if (row) 
    {
        int n_cols = mysql_num_fields(result);
        for (int i = 0; i < n_cols; ++i)
        {
            // 如果字段为空, 传入 "NULL" 字符串
            ret.emplace_back(row[i] == NULL ? "NULL" : row[i]);
        }
    }
    else 
    {
        mysql_free_result(result);
    }

    return ret;
}

bool SqlConnection::lock_read(const std::string& table_name)
{
    std::string stmt_str = "LOCK TABLES " + get_db() + "." + table_name + " READ";
    if (mysql_query(mysql, stmt_str.c_str()))
    {
        std::cout << mysql_error(mysql) << std::endl;
        return false;
    }
    return true;
}

bool SqlConnection::lock_write(const std::string& table_name)
{
    std::string stmt_str = "LOCK TABLES " + get_db() + "." + table_name + " WRITE";
    if (mysql_query(mysql, stmt_str.c_str()))
    {
        std::cout << mysql_error(mysql) << std::endl;
        return false;
    }
    return true;
}

void SqlConnection::unlock()
{
    if (mysql_query(mysql, "UNLOCK TABLES"))
    {
        std::cout << mysql_error(mysql) << std::endl;
    }
}